var GlobalErrorListener = require('./GlobalErrorListener');
var dataUtils = require('./dataUtils');
var r = null;
var o = null;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var deepCopy = dataUtils.deepCopy;
var DataGroup = function (e, t, i, n, a, r) {
    this._data = t;
    this._innerData = i;
    this._innerDataExclude = n;
    this._comp = e;
    this._updateCb = r;
    this._beforeMergeCb = null;
    this._beforeUpdateCb = null;
    this._hidingValue = false;
    this._changes = [];
    this._doingUpdates = null;
    this._propFields = a._propFields;
    this._observers = a._observers;
    this._observerTree = a._observerTree;
    this._relatedObserverTreeObj = a;
    this._observerStatus = [];
};
DataGroup._advancedCreate = function (e, t, i, n, a, r) {
    return new DataGroup(e, t, i, n, a, r);
};
var A = function (e) {
    this._propFields = e;
    this._observerIdInc = 0;
    this._observers = [];
    this._observerTree = {};
};
DataGroup._createObserverTree = function (e) {
    return new A(e);
};
DataGroup.create = function (e, t, i) {
    var n = new A({});
    return new DataGroup({ __methodCaller: e }, t, null, null, n, i);
};
DataGroup.setPropUpdater = function (e) {
    r = e;
};
DataGroup.setPropObserver = function (e) {
    o = e;
};
DataGroup.prototype.replaceWholeData = function (e, t) {
    this._data = e;
};
DataGroup.prototype.setHidingValue = function (e) {
    this._hidingValue = !!e;
};
DataGroup.prototype.setBeforeMergeCb = function (e) {
    this._beforeMergeCb = e;
};
DataGroup.prototype.setBeforeUpdateCb = function (e) {
    this._beforeUpdateCb = e;
};
var c = function (e, t, i, a) {
    var r = e;
    for (var o = 0; o < t.length; o++) {
        var s = t[o];
        if ('**' === s) {
            GlobalErrorListener.hasGlobalWarningListeners() && o !== t.length - 1 && GlobalErrorListener.triggerWarning('"**" can only be used at the end of a path descriptor (in "' + a.is + '").');
            r['**'] || (r['**'] = []);
            r['**'].push(i);
            return;
        }
        r[s] || (r[s] = {});
        r = r[s];
    }
    r['~'] || (r['~'] = []);
    r['~'].push(i);
};
var h = function (e, t, i, n) {
    for (var a = 0; a < t.length; a++)
        c(e, t[a], i, n);
};
var u = function (e, t, i) {
    var n = e;
    var a = 0;
    for (var r = ''; a < t.length && '**' !== (r = t[a]); a++) {
        if (!n)
            return;
        n = n[r];
    }
    var o = '**' === r ? n['**'] : n['~'];
    if (o)
        for (a = 0; a < o.length; a++)
            if (o[a] === i) {
                o.splice(a, 1);
                break;
            }
};
var p = function (e, t, i) {
    for (var n = 0; n < t.length; n++)
        u(e, t[n], i);
};
A.prototype.addObserver = function (e, t, i) {
    var n = this._observerIdInc++;
    this._observers[n] = {
        pathObj: t,
        func: e
    };
    h(this._observerTree, t, n, i);
    return n;
};
A.prototype.removeObserver = function (e) {
    var t = this._observers[e];
    p(this._observerTree, t.pathObj, e);
    this._observers[e] = null;
};
A.prototype.moveObserver = function (e, t) {
    var i = this._observers[e];
    p(this._observerTree, i.pathObj, e);
    i.pathObj = t;
    h(this._observerTree, t, e);
};
DataGroup.prototype.getObserverTree = function () {
    return this._relatedObserverTreeObj;
};
var g = function (e, t, i) {
    var a = [];
    var r = i.pathObj;
    for (var o = 0; o < r.length; o++) {
        var s = r[o];
        var l = t;
        for (var d = 0; d < s.length; d++) {
            var A = s[d];
            if ('**' === A)
                break;
            if ('object' != typeof l) {
                l = undefined;
                break;
            }
            l = l[A];
        }
        a[o] = l;
    }
    GlobalErrorListener.safeCallback('Data Observer', i.func, e.__methodCaller, a, e);
};
var _ = function (e, t) {
    var i = null;
    var n = 0;
    for (var a in e)
        if ('~' !== a)
            if ('**' !== a)
                _(e[a], t);
            else
                for (i = e['**'], n = 0; n < i.length; n++)
                    t[i[n]] = true;
        else
            for (i = e['~'], n = 0; n < i.length; n++)
                t[i[n]] = true;
};
var f = function (e, t, i) {
    var n = e;
    for (var a = 0; a < i.length; a++) {
        if (n['**'])
            for (var r = n['**'], o = 0; o < r.length; o++) {
                t[r[o]] = true;
            }
        var l = i[a];
        if ('number' == typeof l) {
            if (!hasOwnProperty.call(n, l)) {
                n = null;
                break;
            }
        } else if (!/^[_a-zA-Z]/.test(l) || !hasOwnProperty.call(n, l)) {
            n = null;
            break;
        }
        n = n[l];
    }
    n && _(n, t);
};
var v = function (e, t, i, n, a) {
    for (var r = 0; r < e.length; r++) {
        var o = e[r];
        var s = t[r];
        if (s) {
            if (s) {
                t[r] = false;
                g(i, a ? undefined : n, o);
            };
        };
    }
};
DataGroup.prototype.scheduleReplace = function (e, t, i) {
    this._changes.push([
        e,
        t,
        i
    ]);
};
DataGroup.prototype.setChanges = function (e) {
    this._changes = e;
};
DataGroup.prototype.getChanges = function () {
    return this._changes;
};
DataGroup.prototype.doUpdates = function (e, t) {
    var i = this._propFields;
    var n = this._comp;
    var a = this._hidingValue;
    var d = this._innerDataExclude;
    var A = !!this._doingUpdates;
    var c = this._relatedObserverTreeObj._observerIdInc;
    var h = null;
    var u = null;
    var p = null;
    if (c) {
        A || (this._doingUpdates = {
            changedPaths: [],
            changedValues: [],
            combinedChanges: [],
            changesTotal: 0
        });
        h = this._doingUpdates.changedPaths;
        u = this._doingUpdates.changedValues;
        p = this._doingUpdates.combinedChanges;
    } else {
        h = [];
        u = [];
        p = [];
    }
    var g = this._changes;
    this._changes = [];
    var _ = [];
    this._beforeMergeCb && this._beforeMergeCb(g, _, e);
    for (var w = 0; w < g.length; w++) {
        var m = g[w];
        var I = m[0];
        var b = m[1];
        var C = b;
        var y = undefined;
        var E = I[0];
        var x = !!d && d.test(E);
        var B = i[E];
        if (B && 1 === I.length) {
            y = this._data[E];
            b = r.call(this._comp, I, B, b, this._hidingValue);
            if (!x) {
                if (this._innerData) {
                    C = deepCopy(b);
                    _[w] || (this._innerData[E] = C);
                } else {
                    C = b;
                }
                m[1] = C;
            };
            x && !this._innerData || (this._data[E] = b);
        } else {
            var S = this._data;
            var Q = E;
            var k = null;
            var D = 1;
            if (!x || this._innerData) {
                for (; D < I.length; D++)
                    'number' == typeof (k = I[D]) && isFinite(k) ? hasOwnProperty.call(S, Q) && S[Q] instanceof Array || (S[Q] = []) : (!hasOwnProperty.call(S, Q) || null === S[Q] || 'object' != typeof S[Q] || S[Q] instanceof Array) && (S[Q] = {}), S = S[Q], Q = k;
                S[Q] = b;
            }
            if (this._innerData && (S = this._innerData, Q = E, !x)) {
                for (D = 1; D < I.length; D++)
                    'number' == typeof (k = I[D]) && isFinite(k) ? hasOwnProperty.call(S, Q) && S[Q] instanceof Array || (S[Q] = []) : (!hasOwnProperty.call(S, Q) || null === S[Q] || 'object' != typeof S[Q] || S[Q] instanceof Array) && (S[Q] = {}), S = S[Q], Q = k;
                C = deepCopy(b);
                _[w] || (S[Q] = C);
                m[1] = C;
            }
        }
        f(this._observerTree, this._observerStatus, I);
        if (!x) {
            h.push(I);
            u.push([
                C,
                y,
                b
            ]);
            p.push(m);
        };
        c && (this._doingUpdates.changesTotal += 1);
    }
    if (!A) {
        this._beforeUpdateCb && this._beforeUpdateCb(e);
        if (c) {
            var M = 0;
            do {
                M = this._doingUpdates.changesTotal;
                v(this._observers, this._observerStatus, n, this._data, a);
            } while (M !== this._doingUpdates.changesTotal);
            this._doingUpdates = null;
        }
        this._updateCb(h, u, p, e);
        for (var N = 0; N < u.length; N++) {
            var T = u[N];
            if (T) {
                var R = h[N];
                var L = i[R[0]];
                L && o.call(n, T[2], T[1], R, L, a, t);
            }
        }
    }
};
module.exports = DataGroup;
